package org.dlsu.mt.parser;
import java.util.HashSet;
import java.util.Iterator;
import java.util.StringTokenizer;
import java.util.Vector;

/**
 * The FStructureElement represents an attribute inside an FStructure that is able to contain multiple atomic values, represented by the interface FStructureElementValue.
 * The implementation of FStructureInterface's merge method merely compares whether the FStructureElement values matches with the target one.
 * If it is consistent it succeeds otherwise it fails. This class's implementation of merge does not support the merging of FStructure and FStructureElement,
 * if such a merge is needed use the target's FStructure class's merge method.
 */
public class FStructureElement implements FStructureInterface {
	/** The name of the FStructureElement */
	public String name;
	/** The list of FStructureElementValues regardless of order */
	public HashSet values;

	public String prevParentPosition;

	public boolean efPopped = false;
	public boolean fePopped = false;

	public String plurality = "sing";
	//protected String prevValue;

	/**
	 * Creates a new FStructureElement with the given name
	 *
	 * @param name The name of the FStructureElement
	 */
	public FStructureElement(String name) {
		this.name = name;
		prevParentPosition = "";
		values = new HashSet();
	}

	/**
	 * Creates a new FStructureElement given the list of values
	 *
	 * @param name The name of the FStructureElement
	 * @param values The list of FStructureElementValue
	 */
	public FStructureElement(String name, HashSet values) {
		this.name = name;
		this.values = values;
	}

	/**
	 * Adds an FStructureElementValue into the FStructureElement
	 *
	 * @param value FStructureElementValue to add
	 */
	public void addElement(FStructureElementValue value) {
		values.add(value);
	}


	/**
	 * Retrieves the list of FStructureElementValues
	 *
	 * @return The set of values inside the FStructureElement as a HashSet
	 */

	public void setWord(Word w)
	{
		//System.out.println("test fx : " + w.getCurrentPOSTag());
	}

	public HashSet getValues() {
		return values;
	}

	/**
	 * Returns the string representation of the FStructueElement in the form:
	 * <pre><FStructureElement name>:<value1>,<value2>,...,<valueN></pre>
	 *
	 * @return The String representation of the FStructure
	 */
	public String toString() {
		Iterator i;
		StringBuffer sb = new StringBuffer();

		i = values.iterator();
		sb.append(name);
		sb.append(":");
		for(int j=0;j<values.size()-1;j++){

			sb.append(i.next() + ",");
		}
		sb.append(i.next());

		//System.out.println(sb + " hindi abot");

		return sb.toString();
	}

	/**
	 * Sets the name of the FStructureElement
	 *
	 * @param The new name of the FStructureElement
	 */
	public void setName(String name) {
		this.name = name;
	}

	public void setPrevParentPosition(String prevVal){
		prevParentPosition = prevVal;
	}

	public String getPrevParentPosition(){
		return prevParentPosition;
	}

	/**
	 * Returns the XML representation of the FStructureElement in the form:
	 * <attribute name="<FStructureElement name>" type="element">
	 *	<FStructureElementValue1>
	 *	<FStructureElementValue2>
	 *	...
	 *	<FStructureElementValueN>
	 * </attribute>
	 *
	 * @return The XML representation of the FStructure
	 */
	public String toXML() {
		Iterator i;
		StringBuffer sb = new StringBuffer();

if(Word.vPOS.contains(name))
{
		i = values.iterator();
		sb.append("<attribute name=\""+name+"\" type=\"element\">\n");
		while(i.hasNext())
		{
			sb.append( ((FStructureElementValue) i.next()).toXML());
		}
		sb.append("</attribute>\n");
}
		return sb.toString();
	}

	/**
	 * Compares whether two FStructureElement are equal.
	 *
	 * @param obj FStructureElement to compare
	 * @return True if the values and the name of the FStructureElement are the same
	 *
	 *
	 *
	 * Modified by Marlon Tong
	 */
	public boolean equals(Object obj) {
		FStructureElement feat;
		try {
			feat = (FStructureElement) obj;

		}catch(Exception e) {
			System.out.println("Unable to cast "+obj.getClass().getName()+" to FStructureElement in FStructureElement.equals()");
//			e.printStackTrace();
			return false;
		}
		boolean isVerb;
		if((name.equals("verb") || feat.getName().equals("verb")) && (name.contains("noun")|| feat.getName().contains("noun")))
		{
			String[] nounSem, verbSem;
			if(name.equals("verb"))
			{
				nounSem = feat.getSemantics();
				verbSem = getSemantics();
				isVerb = true;
			}
			else
			{
				nounSem = getSemantics();
				verbSem = feat.getSemantics();
				isVerb = false;
			}

			if(!(verbSem.length == 0))
			{
				//GIANAHOO System.out.println("checking semantics...");
				for(int x=0;x<verbSem.length;x++)
					for(int y=0;y<nounSem.length;y++)
					{
						//System.out.println("compare " + verbSem[x] + " with " + nounSem[y]);
						if(verbSem[x].equals(nounSem[y]))
						{
							//GIANAHOO System.out.println("semantics match!");
							return true;
						}
					}
			}
			else
			{
				if(!isVerb)
					removeSem();
				else
					feat.removeSem();
				return true;
			}
		}
		else
			return (feat.values.equals(values) && feat.name.equalsIgnoreCase(name));
		return false;
	}


	/**
	 * Added by Marlon Tong
	 * returns the semantic information found in the FStructureElement
	 */
	public String[] getSemantics()
	{
		Vector<String> v = new Vector();
		Iterator i = values.iterator();
		while(i.hasNext())
		{
			FStructureElementValue f = (FStructureElementValue)i.next();
			String value = f.toString();
			StringTokenizer tokens = new StringTokenizer(value, "\t");
			if(tokens.hasMoreTokens()&&tokens.nextToken().equals("SEM"))
				v.addElement(tokens.nextToken());
		}
		String[] temp;
		if(!v.isEmpty())
		{
			temp = new String[v.size()];
			while(!v.isEmpty())
			{
				temp[v.size()-1] = v.remove(0);
			}
			return temp;
		}
		else
			return new String[0];

	}

	public String[] getFunctions()
	{
		Vector<String> v = new Vector();
		Iterator i = values.iterator();
		while(i.hasNext())
		{
			FStructureElementValue f = (FStructureElementValue)i.next();
			String value = f.toString();
			StringTokenizer tokens = new StringTokenizer(value, "\t");
			if(tokens.hasMoreTokens()&&tokens.nextToken().equals("FUNC"))
				v.addElement(tokens.nextToken());
		}
		String[] temp;
		if(!v.isEmpty())
		{
			temp = new String[v.size()];
			//GIANAHOO System.out.println("functions found:");
			while(!v.isEmpty())
			{
				temp[v.size()-1] = v.remove(0);
				//GIANAHOO System.out.println(temp[v.size()]);
			}
			return temp;
		}
		else
			return null;
	}

	public Vector<SemEntry> getSemEntries()
	{
		Vector<SemEntry> list = new Vector();
		Iterator i = values.iterator();
		while(i.hasNext())
		{
			FStructureElementValue f = (FStructureElementValue)i.next();
			String value = f.toString();
			StringTokenizer tokens = new StringTokenizer(value, "\t");
			String temp = tokens.nextToken();
			String att = null;
			String[] target = null;
			StringTokenizer chop = new StringTokenizer(temp, " ");
			if(chop.countTokens() == 1)
				att = chop.nextToken();
			else
			{
				//GIANAHOO System.out.println("target found");
				target = new String[chop.countTokens()-1];
				int x=0;
				while(chop.hasMoreTokens())
				{
					String tok = chop.nextToken();
					if(!chop.hasMoreTokens())
					{
						att = tok;
					}
					else
					{
						target[x] = tok;
					}
					x++;
				}
			}
			String val = tokens.nextToken();
			if(!att.equals("FUNC")&&!att.equals("PRED")&&!att.equals("SEM"))
				if(target != null)
					list.add(new SemEntry(target, att, val));
				else
					list.add(new SemEntry(att, val));
		}

		if(!list.isEmpty())
			return list;
		return null;
	}

	public String[] chopFunction(String func)
	{
		if(func!=null)
			func = func.substring(1,func.length()-1);

		StringTokenizer tokens = new StringTokenizer(func, ", ");
		String temp = null;
		String[] list = new String[tokens.countTokens()];
		int x=0;
		if(list.length!=0)
			while(tokens.hasMoreTokens())
			{
				temp = tokens.nextToken();
				list[x] = temp;
				x++;
			}

		return list;
	}

	public Vector<String[]> getOrderedFunctions()
	{
		String[] list = getFunctions();
		Vector<String[]> temp = null;
		if(list != null)
		{
			temp = new Vector();
			for(int i=0;i<list.length;i++)
			{
				temp.add(chopFunction(list[i]));
			}

			temp = orderFunctions(temp);

			return temp;
		}

		return null;
	}

	public Vector<String[]> orderFunctions(Vector<String[]> list)
	{
		int highest=0, index=0;

		if(list!=null)
		{
			Vector<String[]> temp = new Vector();

			int num = list.size();
			while(!list.isEmpty())
			{
				highest = 0;
				index = 0;

				for(int z=0; z<list.size(); z++)
				{
					if(z == 0)
					{
						highest = list.get(z).length;
						index = z;
					}
					else
					{
						if(list.get(z).length > highest)
						{
							highest = list.get(z).length;
							index = z;
						}
					}
				}

				temp.add(list.remove(index));
			}

			return temp;
		}

		return null;
	}

	/**
	 * Merges an FStructureElement. Merging only does checking whether the two FStructureElement are equal.
	 *
	 * @param f FStructureElement to merge
	 * @return True when both the FStructureElement are equal, false otherwise
	 */
	public boolean merge(FStructureInterface f) {
		//System.out.println("merging FStructureElement " + name + " with " + f.getClass().getSimpleName()+ " " + f.getName());
		boolean match = f.equals(this);
		if((name.equals("noun") || f.getName().equals("noun")) && (name.equals("verb") || f.getName().equals("verb")) &&match)
			mergeSem((FStructureElement)f);

		return match;
	}

	/**
	 * Function added by Marlon Tong
	 *
	 */
	 public String getSemMatch(FStructureElement f)
	 {
	 	String[] elem1Sem = getSemantics();
	 	String[] elem2Sem = f.getSemantics();
	 	for(int x=0;x<elem1Sem.length;x++)
			for(int y=0;y<elem2Sem.length;y++)
				if(elem1Sem[x].equals(elem2Sem[y]))
					return elem1Sem[x];
		return null;
	 }

	 /**
	  * Function added by Marlon Tong
	  */
	 public void mergeSem(FStructureElement f)
	 {
	 	String match = getSemMatch(f);
	 	HashSet temp = new HashSet();
	 	Iterator i = values.iterator();
	 	while(i.hasNext())
	 	{
	 		FStructureElementValue fv = (FStructureElementValue)i.next();
			StringTokenizer tokens = new StringTokenizer(fv.toString(), "\t");
			if(!tokens.nextToken().equals("SEM"))
				temp.add(fv);
	 	}
	 	if(match!= null)
	 		temp.add(new FStructureElementString("SEM\t"+match));
	 	values = temp;
	 }

	 public String combineFunc(String[] func)
	 {
	 	StringBuffer temp = new StringBuffer();
	 	temp.append("(");
	 	if(func!=null)
	 	{
	 		for(int i=0;i<func.length;i++)
		 	{
		 		temp.append(func[i] + ", ");
		 	}
		 	if(temp.length() > 3)
		 	{
		 		temp.deleteCharAt(temp.length()-1);
		 		temp.deleteCharAt(temp.length()-1);
		 	}
	 	}
	 	temp.append(")");

	 	return temp.toString();
	 }

	 public void removeFunctions(String[] func)
	 {
	 	String match = combineFunc(func);
	 	HashSet temp = new HashSet();
	 	Iterator i = values.iterator();
	 	while(i.hasNext())
	 	{
	 		FStructureElementValue fv = (FStructureElementValue)i.next();
			StringTokenizer tokens = new StringTokenizer(fv.toString(), "\t");
			if(!tokens.nextToken().equals("FUNC"))
			{
				temp.add(fv);
			}
	 	}
	 	if(match!= null)
	 		temp.add(new FStructureElementString("FUNC\t"+match));
	 	values = temp;
	 }

	 /**
	  * Function added by Marlon Tong
	  */
	  public void removeSem()
	  {
	  	HashSet temp = new HashSet();
	  	Iterator i = values.iterator();
	  	while(i.hasNext())
	  	{
	  		FStructureElementValue fv = (FStructureElementValue)i.next();
	  		if(!fv.toString().contains("SEM"))
	  			temp.add(fv);
	  	}
	  	values = temp;
	  }

	  public boolean compareSemEntry(SemEntry sem)
	  {
	  		String att = sem.getAttribute();
		 	Iterator i = values.iterator();
		 	while(i.hasNext())
		 	{
		 		FStructureElementValue fv = (FStructureElementValue)i.next();
				StringTokenizer tokens = new StringTokenizer(fv.toString(), "\t");
				if(tokens.nextToken().equals(att))
				{
					if(tokens.nextToken().equals(sem.getValue()))
						return true;
					else
						return false;
				}
		 	}
		 	return false;
	  }

	  public void assignSemEntry(SemEntry sem)
	  {
	  		String match = sem.getAttribute() + '\t' + sem.getValue();
	  		HashSet temp = new HashSet();
		 	Iterator i = values.iterator();
		 	while(i.hasNext())
		 	{
		 		FStructureElementValue fv = (FStructureElementValue)i.next();
				StringTokenizer tokens = new StringTokenizer(fv.toString(), "\t");
				if(!tokens.nextToken().equals(sem.getAttribute()))
					temp.add(fv);
		 	}
		 	if(match!= null)
		 		temp.add(new FStructureElementString(match));
		 	values = temp;
	  }

	  public boolean compareSemAttribute(SemEntry sem)
	  {
	  		String att = sem.getAttribute();
		 	Iterator i = values.iterator();
		 	while(i.hasNext())
		 	{
		 		FStructureElementValue fv = (FStructureElementValue)i.next();
				StringTokenizer tokens = new StringTokenizer(fv.toString(), "\t");
				if(tokens.nextToken().equals(att))
				{
					return true;
				}
		 	}
		 	return false;
	  }

	/**
	 * Gets the semantic features of a particular word such as NUM or PRED stored in the database
	 *
	 * @return Array of strings containing the semantic features
	 */
	public String[] getFeatures() {

		Iterator i = values.iterator();
	 	String feats[] = new String[values.size()];
	 	int n=0;

		while(i.hasNext()) {
	 		FStructureElementValue fv = (FStructureElementValue)i.next();

			StringTokenizer tokens = new StringTokenizer(fv.toString(), "\t");

			if(tokens.hasMoreElements()) {
				feats[n] = tokens.nextToken() + "=" + tokens.nextToken();
				n++;
			}

	 	}

		return feats;
	}

	/**
	 * Gets all the semantic feature-value pairs of the FStructureElement.
	 * This is an implementation of the method declared in FStructureInterface to hide the fact that the attribute name for the HashSet is different among the different implementing classes.
	 * @return The HashSet of the feature-value pairs
	 */
	public HashSet getFeaturesAlias() {
		return values;
	}

	public String getPlurality() {
		return plurality;
	}

	public void setPlurality(String plurality) {
		this.plurality = plurality;
	}

	/**
	 * Returns the name of the FStructureElement
	 *
	 * @return The name of the FStructureElement
	 */
	public String getName() {
		return name;
	}

	/*
	public String getPrevValue(){
		return prevValue;
	}

	public void setPrevValue(String val){

		prevValue = val;
	}
	*/
	/**
	 * Creates a copy or a clone of the FStructureElement including all the values inside it
	 *
	 * @return A copy of the current FStructureElement object
	 */
	public Object clone() {
		FStructureElement f;
		Iterator i;
		f = new FStructureElement(name);
		f.setPrevParentPosition(prevParentPosition);
		i = values.iterator();

		while(i.hasNext())
			f.values.add( ((FStructureElementValue) i.next()).clone() );

		return f;
	}
}